﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MadLibrary.Collections;

namespace MadLibrary.Web
{
    /// <summary>
    /// Holds styling properties and their targets.
    /// </summary>
    public class CssStyle : IBuildable, IParsable
    {
        /// <summary>
        /// Gets or sets collection of CSS style pointers/element selectors.
        /// </summary>
        public Collection<CssGroup> Selectors { get; set; }
        /// <summary>
        /// Gets or sets CSS properties of current CSS style.
        /// </summary>
        public CssProperties Properties { get; set; }
        /// <summary>
        /// Gets or sets pseudo class of current CSS style.
        /// </summary>
        public CssGroup PseudoClass { get; set; }
        /// <summary>
        /// Creates new empty CSS style.
        /// </summary>
        public CssStyle()
        {
            this.Selectors = new Collection<CssGroup>();
            this.PseudoClass = null;
            this.Properties = new CssProperties();
        }
        /// <summary>
        /// Creates new CSS style with specified selectors.
        /// </summary>
        /// <param name="PreSelectors">Specifies selectors of new CSS style.</param>
        public CssStyle(params CssGroup[] PreSelectors)
        {
            this.Selectors = new Collection<CssGroup>(PreSelectors);
            this.PseudoClass = null;
            this.Properties = new CssProperties();
        }
        /// <summary>
        /// Creates new CSS style with specified selectors and specified presudo class.
        /// </summary>
        /// <param name="PreSelectors">Specifies selectors of new CSS style.</param>
        /// <param name="PseudoClass">Specifies pseudo class of new CSS style.</param>
        public CssStyle(CssGroup[] PreSelectors, CssGroup PseudoClass)
        {
            this.Selectors = new Collection<CssGroup>(PreSelectors);
            this.PseudoClass = PseudoClass;
            this.Properties = new CssProperties();
        }
        /// <summary>
        /// Creates new CSS style with specified selectors, specified pseudo class and specified property collection.
        /// </summary>
        /// <param name="PreSelectors">Specifies selectors of new CSS style.</param>
        /// <param name="PseudoClass">Specifies pseudo class of new CSS style.</param>
        /// <param name="Properties">Specifies collection of properties for new CSS style.</param>
        public CssStyle(CssGroup[] PreSelectors, CssGroup PseudoClass, CssProperties Properties)
        {
            this.Selectors = new Collection<CssGroup>(PreSelectors);
            this.PseudoClass = PseudoClass;
            this.Properties = Properties;
        }
        /// <summary>
        /// Creates new CSS style with specified selectors, specified pseudo class and specified CSS propery array.
        /// </summary>
        /// <param name="PreSelectors">Specifies selectors of new CSS style.</param>
        /// <param name="PseudoClass">Specifies pseudo class of new CSS style.</param>
        /// <param name="Properties">Specifies array of properties for new CSS style.</param>
        public CssStyle(CssGroup[] PreSelectors, CssGroup PseudoClass, CssProperty[] Properties)
        {
            this.Selectors = new Collection<CssGroup>(PreSelectors);
            this.PseudoClass = PseudoClass;
            this.Properties = new CssProperties(Properties);
        }

        /// <summary>
        /// Generates refactorized string of current CssStyle object instance.
        /// </summary>
        /// <returns>Generated string.</returns>
        string IBuildable.Build()
        {
            string ret = "";
            string pss = "";
            foreach(CssGroup g in this.Selectors)
            {
                pss += (g as IBuildable).Build();
            }
            if(this.PseudoClass!=null)
                pss += (this.PseudoClass as IBuildable).Build();
            string propstr = "";
            foreach(CssProperty p in this.Properties)
            {
                propstr += (p as IRefactorBuildable).Build(0) + Environment.NewLine;
            }
            ret += pss + "\r\n{\r\n" + propstr + "}\r\n";
            if (ret.StartsWith(" "))
                ret = ret.Remove(0, 1);
            return ret + Environment.NewLine;
        }

        /// <summary>
        /// Parses specified input string into current CssStyle object instance.
        /// </summary>
        /// <param name="input">Specifies string which has to be parsed.</param>
        void IParsable.Parse(string input)
        {
            Match m = Regex.Match(input, "([^:]+):([a-zA-z]+)[^{}]*{([^{}]*)}");
            string propraw = "";
            if (m.Success)
            {
                this.PseudoClass = new CssGroup( m.Groups[2].Value, CssGroupType.PseudoClass);
                input = m.Groups[1].Value;
                propraw = m.Groups[3].Value;
            }
            MatchCollection mc = Regex.Matches(input, "(\\.[a-zA-Z]+|#[a-zA-Z]+|\\s[a-zA-Z]+|[a-zA-Z]+)");
            if (mc.Count != 0)
            {
                foreach (Match a in mc)
                {
                    CssGroup g = new CssGroup();
                    (g as IParsable).Parse(m.Groups[1].Value);
                    this.Selectors.Add(g);
                }
            }
            else throw new InvalidCssInputException("Invalid string input passed for parsing.");
            mc = Regex.Matches(propraw, "([^;]*);");
            foreach (Match c in mc)
            {
                CssProperty a = new CssProperty();
                (a as IParsable).Parse(c.Groups[1].Value + ";");
                this.Properties.Add(a);
            }
        }
    }
}
